home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 1 / Gold Medal Software Volume 1 (Gold Medal) (1994).iso / prog / cextract.arj / CEXTRACT.TAR / cextract / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-08  |  23.8 KB  |  891 lines

  1. /*
  2.  *
  3.  * The purpose of this program is to extract the function descriptions
  4.  * (prototypes) from C source code.  It also provides for the creation
  5.  * of documentation based on those prototypes.
  6.  *
  7.  * The specific reason for the creation of this program was to
  8.  * provide a method for automatically creating header files to
  9.  * describe all of the functions to be used within a multi-file
  10.  * program.
  11.  *
  12.  * This file makes use of code in the companion files parse.c and io.c.
  13.  *
  14.  * Copyright (c) 1990, 1991, 1992 by Adam Bryant
  15.  *
  16.  * See Copyright notice in the file parse.c or in the manual page.
  17.  *
  18.  */
  19. /*
  20.  * Version history:
  21.  *
  22.  * For Version 0.30:  file parse functions were separated out into a
  23.  *   separate file so that other programs could make use of them.  The
  24.  *   functions descriptions are now stored in dynamic memory and
  25.  *   functions are provided to allow the manipulation of those
  26.  *   descriptions.
  27.  * Version 0.31:  Only a few minor adjustments were made to cextract...
  28.  *   only as much as was needed to build the cextdoc program using the
  29.  *   cproto.[ch] files.
  30.  * Version 0.40:  Totally rewrote the parsing code so that the use of
  31.  *   temporary files is unnecessary.  Instead, two dynamic storage
  32.  *   elements have been created.  Besides being much faster, this
  33.  *   method is also much more elegant than the prior method.
  34.  * Version 0.41:  Switched the NOSETBUFFER to a SETBUFFER flag.
  35.  *   Added in the ability to handle the vararg system to convert it
  36.  *   to the "..." for ANSI C.
  37.  *
  38.  * ... change notes for later versions in parse.c
  39.  *
  40.  * Note on Version 1.0: merged the cextract and cextdoc into one
  41.  *   program, with differential being made based on the command line
  42.  *   or in the program name.  Version sent to comp.sources.reviewed.
  43.  *
  44.  */
  45. #include "xtract.h"
  46. #ifndef VMS
  47. #include <sys/types.h>
  48. #include <sys/stat.h>
  49. #else
  50. #include <types.h>
  51. #include <stat.h>
  52. #endif /* VMS */
  53.  
  54. /* definition strings to be set when the C preprocessor runs */
  55. #ifndef VAXC
  56. #define CPP_GIVEN "-D__CEXTRACT__"
  57. #define CPP_GIVEN2 "-D__CEXTDOC__"
  58. #else
  59. #define CPP_GIVEN "/define=__CEXTRACT__"
  60. #define CPP_GIVEN2 "/define=__CEXTDOC__"
  61. #endif /* VAXC */
  62.  
  63. /* output filter from the parsing routines */
  64. void
  65. out_char(type, outch)
  66.   int type, outch;
  67. {
  68.   /* to keep track of where the output list is */
  69.   int tab_width = get_option(OPT_TABWIDTH);
  70.   static P_BUFDATA curbuf[2] = { NULL, NULL };
  71.   static int curpos[2] = { 0, 0 };
  72.   static int tab_count = 0;
  73.  
  74.   /* split output properly */
  75.   switch (type) {
  76.   case 0:
  77.     /* documentation mode should never get this */
  78.     if (doc_extract != DOC_NONE) {
  79.       fprintf(stderr, "Serious Internal Error: bad out_char() data\n");
  80.       exit(1);
  81.     }
  82.   case 1:
  83.     /* give it to the first one */
  84.     if (tempbuf[type] == NULL) {
  85.  
  86.       /* start the list */
  87.       if ((tempbuf[type] = (P_BUFDATA) malloc(sizeof(S_BUFDATA))) == NULL) {
  88.     fprintf(stderr, "Memory Allocation Error\n");
  89.     exit(1);
  90.       }
  91.       tempbuf[type]->next = NULL;
  92.       curbuf[type] = tempbuf[type];
  93.     }
  94.  
  95.     /* have we got enough in this one? */
  96.     if (curpos[type] == MID_SIZE) {
  97.  
  98.       /* finish it off */
  99.     build_memory:
  100.       curbuf[type]->data[MID_SIZE] = '\0';
  101.       curpos[type] = 0;
  102.  
  103.       /* start the next one */
  104.       if ((curbuf[type]->next =
  105.        (P_BUFDATA) malloc(sizeof(S_BUFDATA))) == NULL) {
  106.     fprintf(stderr, "Memory Allocation Error\n");
  107.     exit(1);
  108.       }
  109.       curbuf[type] = curbuf[type]->next;
  110.       curbuf[type]->next = NULL;
  111.  
  112.     }
  113.  
  114.     /* special output filter for the documentation mode */
  115.     if (doc_extract != DOC_NONE) {
  116.  
  117.       /* tab expansion? */
  118.       if (tab_width > 0) {
  119.     if (outch == '\t') {
  120.       /* fill out tabs properly */
  121.       while (tab_count++ < tab_width) {
  122.  
  123.         /* check that it will fit */
  124.         if (curpos[type] == MID_SIZE) {
  125.           tab_count--;
  126.           goto build_memory;
  127.         }
  128.  
  129.         /* put out the space */
  130.         curbuf[type]->data[(curpos[type])++] = ' ';
  131.  
  132.       }
  133.       tab_count = 0;
  134.       break;
  135.     } else if (outch == '\\') {
  136.       /* account for font changes */
  137.       tab_count -= 2;
  138.     } else if (outch == '\n') {
  139.       /* now on a new line */
  140.       tab_count = 0;
  141.     } else {
  142.       /* just count */
  143.       if (++tab_count == tab_width) {
  144.         tab_count = 0;
  145.       }
  146.     }
  147.       }
  148.  
  149.     }
  150.  
  151.     /* now store it */
  152.     curbuf[type]->data[(curpos[type])++] = outch;
  153.     break;
  154.   case -1:
  155.     /* close everything up */
  156.     if (curbuf[0] != NULL) {
  157.       curbuf[0]->data[curpos[0]] = '\0';
  158.     }
  159.     if (curbuf[1] != NULL) {
  160.       curbuf[1]->data[curpos[1]] = '\0';
  161.     }
  162.     break;
  163.   default:
  164.     /* ain't one of mine */
  165.     fprintf(stderr, "Serious Internal Error: bad data to out_char()\n");
  166.     break;
  167.   }
  168.  
  169. }
  170.  
  171. /* show the version of the program */
  172. void
  173. show_version ()
  174. {
  175.   fprintf(stderr, "%s: version %d.%d, Copyright 1992 by Adam Bryant\n",
  176.       prog_name, VERSION, PATCHLEVEL);
  177. }
  178.  
  179. /* quickly show a sting and indicate if it is on or off */
  180. static void
  181. quick_show (str, mode)
  182.   char *str;
  183.   int mode;
  184. {
  185.   fprintf(stderr, "\t%s is %s.\n", str, mode ? "on":"off");
  186. }
  187.  
  188. /* show all of the settings */
  189. void
  190. show_settings ()
  191. {
  192.   P_MACRO macro_temp;
  193.   P_SUBST sub_tmp;
  194.  
  195.   /* give the version info and current settings */
  196.   show_version();
  197.   fprintf(stderr, "\n  List of current settings:\n");
  198.   fprintf(stderr, "\tC preprocessor in use: %s\n", cpp_prog);
  199.   switch (doc_extract) {
  200.   case DOC_NONE:
  201.     fprintf(stderr, "\tfunction prototype extraction mode.\n");
  202.     if (output_file[0][0] != '\0') {
  203.       fprintf(stderr, "\tthe output file is \"%s\".\n", output_file[0]);
  204.     }
  205.     if (header_string[0] != '\0') {
  206.       fprintf(stderr, "\tthe header string is \"%s\".\n", header_string);
  207.     }
  208.     quick_show("merged ANSI and K&R C output", get_option(OPT_COMPACT));
  209.     if (!get_option(OPT_COMPACT)) {
  210.       quick_show("showing both ANSI and K&R C", get_option(OPT_BOTHUSE));
  211.       if (!get_option(OPT_BOTHUSE)) {
  212.     quick_show("ANSI C output format", get_option(OPT_STDCUSE));
  213.       }
  214.     }
  215.     quick_show("showing prototypes at all times", get_option(OPT_SHOWANYWAY));
  216.     break;
  217.   case DOC_NORMAL:
  218.     fprintf(stderr, "\ttext documentation mode.\n");
  219.     if (output_file[1][0] != '\0') {
  220.       fprintf(stderr, "\tthe output file is \"%s\".\n", output_file[1]);
  221.     }
  222.     break;
  223.   case DOC_ROFF:
  224.     fprintf(stderr, "\ttroff documentation mode.\n");
  225.     if (output_file[1][0] != '\0') {
  226.       fprintf(stderr, "\tthe output file is \"%s\".\n", output_file[1]);
  227.     }
  228.     break;
  229.   default:
  230.     fprintf(stderr, "\tError, unknown mode.\n\n");
  231.     exit(1);
  232.     break;
  233.   }
  234.   if (get_option(OPT_SORTMODE) == SORT_ALL) {
  235.     quick_show("sorting for all functions", TRUE);
  236.   } else if (get_option(OPT_SORTMODE) == SORT_FILE) {
  237.     quick_show("sorting for each file", TRUE);
  238.   } else {
  239.     quick_show("sorting", FALSE);
  240.   }
  241.   if (get_option(OPT_WRAPPOINT) > 0) {
  242.     fprintf(stderr, "\tlong prototype lists wrap at column %d.\n",
  243.         get_option(OPT_WRAPPOINT));
  244.   } else {
  245.     quick_show("wrapping of long prototypes", FALSE);
  246.   }
  247.   quick_show("separating function name and type", get_option(OPT_TYPEWRAP));
  248.   quick_show("showing first comment in each file",
  249.          get_option(OPT_FIRSTCOMMENT));
  250.   quick_show("sending file name with first comment",
  251.          get_option(OPT_PREPEND));
  252.   quick_show("comment capturing", get_option(OPT_COMMENTS));
  253.   if (get_option(OPT_STATICMODE) == ONLY_STATICS) {
  254.     quick_show("only static functions are searched for", TRUE);
  255.   } else {
  256.     quick_show("including static functions in search",
  257.            get_option(OPT_STATICMODE) == ANY_STATICS);
  258.   }
  259.   quick_show("prepending extern to function declarations",
  260.          get_option(OPT_EXTERNS));
  261.   quick_show("testing for multi-line comments",
  262.          !get_option(OPT_SINGLECOMMENTS));
  263.  
  264.   /* now show all of the macro definitions */
  265.   if (macro_list != NULL) {
  266.     fprintf(stderr, "\n  List of preprocessor definitions:\n");
  267.     for (macro_temp = macro_list;
  268.      macro_temp != NULL;
  269.      macro_temp = macro_temp->next) {
  270.       /* use? */
  271.       if ((macro_temp->usewhen != 2) &&
  272.       (macro_temp->usewhen != (doc_extract != DOC_NONE))) continue;
  273.       fprintf(stderr, "\t%s\n", macro_temp->m_str);
  274.     }
  275.   }
  276.  
  277.   /* now check the substitutions */
  278.   if (subst_list != NULL) {
  279.     fprintf(stderr, "\n  List of prototype substitutions:\n");
  280.     for (sub_tmp = subst_list;
  281.      sub_tmp != NULL;
  282.      sub_tmp = sub_tmp->next) {
  283.       /* used? */
  284.       if ((sub_tmp->usewhen != 2) &&
  285.       (sub_tmp->usewhen != (doc_extract != DOC_NONE))) continue;
  286.  
  287.       /* check it */
  288.       switch (sub_tmp->submode) {
  289.       case SUBST_FULL:
  290.     /* the full string needs replacing? */
  291.     fprintf(stderr, "\treplace all \"%s\" with \"%s\"\n",
  292.         sub_tmp->from_str, sub_tmp->to_str);
  293.     break;
  294.       case SUBST_TYPE:
  295.     /* the type only needs replacing? */
  296.     fprintf(stderr, "\treplace type \"%s\" with \"%s\"\n",
  297.         sub_tmp->from_str, sub_tmp->to_str);
  298.     break;
  299.       case SUBST_NAME:
  300.     /* the variable only needs replacing? WHY!? */
  301.     fprintf(stderr, "\treplace name \"%s\" with \"%s\"\n",
  302.         sub_tmp->from_str, sub_tmp->to_str);
  303.     break;
  304.       }
  305.     }
  306.   }
  307.  
  308.   /* all done */
  309.   fprintf(stderr, "\n");
  310.   exit(0);
  311. }
  312.  
  313. /* quick function to copy a "string" */
  314. int
  315. copy_str (o_str, in_str)
  316.   char *o_str, *in_str;
  317. {
  318.   int ch, count = 0, ch_read = 0;
  319.  
  320.   /* find the beginning of the string */
  321.   while ((*in_str != '"') &&
  322.      (*in_str != '\0')) {
  323.     ch_read++;
  324.     in_str++;
  325.   }
  326.  
  327.   /* now was a string found? */
  328.   if (*in_str == '\0') {
  329.     return(-1);
  330.   }
  331.   ch_read++;
  332.   in_str++;
  333.  
  334.   /* copy all of the string */
  335.   while (((ch = *(in_str)) != '\0') &&
  336.      (ch != '"')) {
  337.     /* copy it */
  338.     o_str[count++] = ch;
  339.     ch_read++;
  340.     in_str++;
  341.   }
  342.   if (ch == '"') ch_read++;
  343.  
  344.   /* finish up and get out */
  345.   o_str[count] = '\0';
  346.   return(ch_read);
  347. }
  348.  
  349. /* show the usage for the program */
  350. void
  351. show_usage()
  352. {
  353. #ifndef VMS
  354.   fprintf(stderr,
  355.       "Usage: %s [-Q#] [+AaBbPpNnVvxZ -Hstr -Yprg -o ofile] [[-opts] file ... ]\n",
  356.       prog_name);
  357.   fprintf(stderr, "\t+A        alphabetically sort all functions [incompatable with +C]\n");
  358.   fprintf(stderr, "\t+a        alphabetically sort functions within each file [default off]\n");
  359.   fprintf(stderr, "\t-B        build a system configuration file based on settings\n");
  360.   fprintf(stderr, "\t-b        build a configuration file in the current directory\n");
  361.   fprintf(stderr, "\t+C        include first comment from each file\n");
  362.   fprintf(stderr, "\t+c        include comments immediately preceding functions\n");
  363.   fprintf(stderr, "\t-Dexpr    send a macro definition to the C preprocessor\n");
  364.   fprintf(stderr, "\t+E        add a preceding 'extern' to each function definition\n");
  365.   fprintf(stderr, "\t+F        toggle prepending of file name to first comment [need +C]\n");
  366.   fprintf(stderr, "\t-f#cc     in troff doc-mode set various font styles\n");
  367.   fprintf(stderr, "\t-Hstring  build header file with read test on string\n");
  368.   fprintf(stderr, "\t-Idir     add directory to search list for include files\n");
  369.   fprintf(stderr, "\t-o file   send output to a file instead of standard output\n");
  370.   fprintf(stderr, "\t           [take first non-flag as filename for output]\n");
  371.   fprintf(stderr, "\t+m        allow multi-line comments when parsing comments\n");
  372.   fprintf(stderr, "\t-N        do documentation output in troff ms format\n");
  373.   fprintf(stderr, "\t-n        do documentation output in normal text\n");
  374.   fprintf(stderr, "\t+P        create output in both ANSI and non-ANSI prototypes\n");
  375.   fprintf(stderr, "\t+p        create output in ANSI C prototype format\n");
  376.   fprintf(stderr, "\t-Q#       do not read in config files, or only those specified\n");
  377.   fprintf(stderr, "\t-qfile    read in a specified configuration file\n");
  378.   fprintf(stderr, "\t+r        remove variable names prior to output\n");
  379.   fprintf(stderr, "\t+S        show parameters for non-ANSI portion of output\n");
  380.   fprintf(stderr, "\t+s        include static function definitions\n");
  381.   fprintf(stderr, "\t-s=only   capture only static function definitions\n");
  382.   fprintf(stderr, "\t-s=none   exclude static function definitions\n");
  383.   fprintf(stderr, "\t-s=any    include static function definitions\n");
  384.   fprintf(stderr, "\t-TN       set tab width in doc-mode to N characters [0 = off]\n");
  385.   fprintf(stderr, "\t-Uname    undefine the given macro name\n");
  386.   fprintf(stderr, "\t-V        display the program settings then exit\n");
  387.   fprintf(stderr, "\t-v        display version information then exit\n");
  388.   fprintf(stderr, "\t+W        place function type on a separate line.\n");
  389.   fprintf(stderr, "\t+w#       wrap long parameter output [dflt length 72]\n");
  390.   fprintf(stderr, "\t-x        run program in extraction mode\n");
  391.   fprintf(stderr, "\t-Yprog    specify program to use as a C preprocessor\n");
  392.   fprintf(stderr, "\t+Z        create the output as a compact, merged list\n");
  393. #else /* VMS */
  394.   fprintf(stderr, "USAGE: %s [/READ-CONFIG=#] [/OUTPUT=outfile] [[/OPTS] FILE ... ]\n",
  395.       prog_name);
  396. #endif /* VMS */
  397.   exit(1);
  398. }
  399.  
  400. /* set the value of a global option */
  401. void
  402. set_option(which, how, value)
  403.   Optype which;
  404.   int how, value;
  405. {
  406. #ifdef CHECK_INPUTS
  407.   /* check input values */
  408.   if ((which < 0) ||
  409.       (which >= OPT_NUMBER)) {
  410.     fprintf(stderr, "SERIOUS ERROR: Internal option value out of range %d\n",
  411.         which);
  412.     exit(1);
  413.   }
  414.   if ((how < 0) ||
  415.       (how > 2)) {
  416.     fprintf(stderr, "SERIOUS ERROR: Internal option method out of range %d\n",
  417.         how);
  418.     exit(1);
  419.   }
  420. #endif /* CHECK_INPUTS */
  421.  
  422.   /* now set properly */
  423.   if (how == 2) {
  424.     global_opts[0][which] = value;
  425.     global_opts[1][which] = value;
  426.   } else {
  427.     global_opts[how][which] = value;
  428.   }
  429. }
  430.  
  431. /* retrieve the value of a global option */
  432. int
  433. get_option(which)
  434.   Optype which;
  435. {
  436. #ifdef CHECK_INPUTS
  437.   /* check input values */
  438.   if ((which < 0) ||
  439.       (which >= OPT_NUMBER)) {
  440.     fprintf(stderr, "SERIOUS ERROR: Internal option value out of range %d\n",
  441.         which);
  442.     exit(1);
  443.   }
  444. #endif /* CHECK_INPUTS */
  445.  
  446.   /* return whichever setting is needed */
  447.   if (doc_extract == DOC_NONE) {
  448.     return(global_opts[0][which]);
  449.   }
  450.   return(global_opts[1][which]);
  451. }
  452.  
  453. /* set configuration settings based on the initial name */
  454. static void
  455. cext_init()
  456. {
  457.   /* enter the proper mode */
  458.   if (strncmp(CEXTDOC_NAME, prog_name, strlen(prog_name)) == 0) {
  459.     doc_extract = DOC_NORMAL;
  460.   } else {
  461.     doc_extract = DOC_NONE;
  462.   }
  463.  
  464.   /* set documentation mode options properly */
  465.   set_option(OPT_COMMENTS, 1, TRUE);
  466.   set_option(OPT_STATICMODE, 1, ANY_STATICS);
  467.   set_option(OPT_EXTERNS, 1, FALSE);
  468.   set_option(OPT_STDCUSE, 1, TRUE);
  469.   set_option(OPT_SORTMODE, 1, SORT_FILE);
  470.  
  471.   /* set extraction mode options now */
  472.   set_option(OPT_COMMENTS, 0, FALSE);
  473.   set_option(OPT_STATICMODE, 0, NO_STATICS);
  474.   set_option(OPT_EXTERNS, 0, TRUE);
  475.   set_option(OPT_STDCUSE, 0, FALSE);
  476.   set_option(OPT_SORTMODE, 0, SORT_NONE);
  477.  
  478.   /* now set common options */
  479.   set_option(OPT_NONAMES, 2, FALSE);
  480.   set_option(OPT_COMPACT, 2, FALSE);
  481.   set_option(OPT_TYPEWRAP, 2, FALSE);
  482.   set_option(OPT_WRAPPOINT, 2, 0);
  483.   set_option(OPT_BOTHUSE, 2, TRUE);
  484.   set_option(OPT_SINGLECOMMENTS, 2, TRUE);
  485.   set_option(OPT_FIRSTCOMMENT, 2, FALSE);
  486.   set_option(OPT_SHOWANYWAY, 2, TRUE);
  487.   set_option(OPT_PREPEND, 2, FALSE);
  488.   set_option(OPT_TABWIDTH, 2, 8);
  489. }
  490.  
  491. /* return FALSE if character is not a normal part of a filename */
  492. int
  493. fname_char(ch)
  494.   int ch;
  495. {
  496.   switch (ch) {
  497. #ifdef MS_DOS
  498.   case '\\':
  499. #endif /* MS_DOS */
  500. #ifdef VMS
  501.   case ']':
  502.   case ':':
  503. #endif /* VMS */
  504.   case '/':
  505.     /* nope */
  506.     return(FALSE);
  507.   }
  508.   return(TRUE);
  509. }
  510.  
  511. /* routine to build the macros onto the prototype line */
  512. static void
  513. append_macros(str_out)
  514.   char *str_out;
  515. {
  516.   P_MACRO macro_temp;
  517.  
  518. #ifndef VAXC
  519.   for (macro_temp = macro_list;
  520.        macro_temp != NULL;
  521.        macro_temp = macro_temp->next) {
  522.  
  523.     /* check for use */
  524.     if ((macro_temp->usewhen != 2) &&
  525.     (macro_temp->usewhen != (doc_extract != DOC_NONE))) continue;
  526.  
  527.     /* use it */
  528.     strcat(str_out, macro_temp->m_str);
  529.     strcat(str_out, " ");
  530.  
  531.   }
  532. #else
  533.   char und_str[MID_SIZE], def_str[MID_SIZE], inc_str[MID_SIZE];
  534.   int und_uses = 0, def_uses = 0, inc_uses = 0;
  535.   int und_len = strlen(UNDEF_LEADER);
  536.   int inc_len = strlen(UNDEF_LEADER);
  537.   int def_len = strlen(DEF_LEADER);
  538.  
  539.   /* initialize */
  540.   und_str[0] = '\0';
  541.   def_str[0] = '\0';
  542.   inc_str[0] = '\0';
  543.  
  544.   /* properly build the command line for the C compiler */
  545.   for (macro_temp = macro_list;
  546.        macro_temp != NULL;
  547.        macro_temp = macro_temp->next) {
  548.  
  549.     /* check for use */
  550.     if ((macro_temp->usewhen != 2) &&
  551.     (macro_temp->usewhen != (doc_extract != DOC_NONE))) continue;
  552.  
  553.     /* check for the definition statement */
  554.     if (strncmp(DEF_LEADER, macro_temp->m_str, def_len) == 0) {
  555.       if (def_uses++ > 0) {
  556.     strcat(def_str, ",");
  557.       }
  558.       if (macro_temp->m_str[def_len] == '(') {
  559.     strcat(def_str, macro_temp->m_str + def_len + 1);
  560.     def_str[strlen(def_str) - 1] = '\0';
  561.     def_uses++;
  562.       } else {
  563.     strcat(def_str, macro_temp->m_str + def_len);
  564.       }
  565.     } else if (strncmp(UNDEF_LEADER, macro_temp->m_str, und_len) == 0) {
  566.       if (und_uses++ > 0) {
  567.     strcat(und_str, ",");
  568.       }
  569.       if (macro_temp->m_str[und_len] == '(') {
  570.     strcat(und_str, macro_temp->m_str + und_len + 1);
  571.     und_str[strlen(und_str) - 1] = '\0';
  572.     def_uses++;
  573.       } else {
  574.     strcat(und_str, macro_temp->m_str + und_len);
  575.       }
  576.     } else if (strncmp(INC_LEADER, macro_temp->m_str, inc_len) == 0) {
  577.       if (inc_uses++ > 0) {
  578.     strcat(inc_str, ",");
  579.       }
  580.       if (macro_temp->m_str[inc_len] == '(') {
  581.     strcat(inc_str, macro_temp->m_str + inc_len + 1);
  582.     und_str[strlen(inc_str) - 1] = '\0';
  583.     def_uses++;
  584.       } else {
  585.     strcat(inc_str, macro_temp->m_str + inc_len);
  586.       }
  587.     } else {
  588.       strcat(str_out, macro_temp->m_str);
  589.     }
  590.  
  591.   }
  592.  
  593.   /* now finish everything off */
  594.   if (def_uses > 0) {
  595.     strcat(str_out, DEF_LEADER);
  596.     if (def_uses > 1) strcat(str_out, "(");
  597.     strcat(str_out, def_str);
  598.     if (def_uses > 1) strcat(str_out, ")");
  599.   }
  600.   if (und_uses > 0) {
  601.     strcat(str_out, UNDEF_LEADER);
  602.     if (und_uses > 1) strcat(str_out, "(");
  603.     strcat(str_out, und_str);
  604.     if (und_uses > 1) strcat(str_out, ")");
  605.   }
  606.   if (inc_uses > 0) {
  607.     strcat(str_out, INC_LEADER);
  608.     if (inc_uses > 1) strcat(str_out, "(");
  609.     strcat(str_out, inc_str);
  610.     if (inc_uses > 1) strcat(str_out, ")");
  611.   }
  612.   strcat(str_out, " ");
  613. #endif /* VAXC */
  614. }
  615.  
  616. /* control program flow and command line options */
  617. int
  618. main (argc, argv)
  619.   int argc;
  620.   char **argv;
  621. {
  622.   char cur_argstr[MID_SIZE], sysl_out[MID_SIZE];
  623.   extern int getpid();
  624.   struct stat fst;
  625.   int count, j, len;
  626.  
  627.   /* initialize some things */
  628.   fpout = stdout;
  629.   len = strlen(argv[0]);
  630.   for (j = len - 1; j > 0; j--) {
  631.     if (fname_char(argv[0][j]) == FALSE) {
  632.       j++;
  633.       break;
  634.     }
  635.   }
  636.   strcpy(prog_name, &(argv[0][j]));
  637.   out_filenum = 0;
  638.   start_comment[0] = '\0';
  639.   header_string[0] = '\0';
  640.   cur_cfg_file[0] = '\0';
  641.   output_file[0][0] = '\0';
  642.   output_file[1][0] = '\0';
  643.   cfg_file[0] = '\0';
  644.   sprintf(cpp_prog, "%s %s", CPP, CPP_COMMENTS);
  645.   macro_list = last_macro = NULL;
  646.   cext_init();
  647.  
  648.   /* check for config file flags */
  649.   count = 1;
  650.   if ((count < argc) &&
  651.       is_switch(argv[count][0])) {
  652.  
  653. #ifndef VMS
  654.     /* are we skipping config files? */
  655.     if (argv[count][1] == 'Q') {
  656.  
  657.       /* check the type */
  658.       if (isdigit(argv[count][2])) {
  659.     cfg_switch = argv[count][2] - '0';
  660.     if ((cfg_switch < 0) ||
  661.         (cfg_switch > 7) ||
  662.         (argv[count][3] != '\0')) {
  663.       fprintf(stderr, "Invalid value for -Q flag\n");
  664.       show_usage();
  665.     }
  666.       } else if (argv[count][2] != '\0') {
  667.     fprintf(stderr, "Invalid data following the -Q flag\n");
  668.     show_usage();
  669.       } else {
  670.     cfg_switch = 0;
  671.       }
  672.  
  673.       /* processed this one */
  674.       count++;
  675.     } else {
  676. #endif /* VMS */
  677.  
  678.       /* now test for full length string */
  679.       if (minmatch_str(&(argv[count][1]), "read-config", 4)) {
  680.     len = strlen(argv[count]);
  681.     for (j = 4; j < len; j++) {
  682.       if ((argv[count][j] == ':') ||
  683.           (argv[count][j] == '=')) {
  684.         j++;
  685.         break;
  686.       }
  687.     }
  688.     if (j == len) {
  689.       cfg_switch = 0;
  690.     } else if (isdigit(argv[count][j])) {
  691.       cfg_switch = argv[count][j] - '0';
  692.       if ((cfg_switch < 0) ||
  693.           (cfg_switch > 7) ||
  694.           (argv[count][j + 1] != '\0')) {
  695.         fprintf(stderr, "Invalid value for -read-config flag\n");
  696.         show_usage();
  697.       }
  698.     } else {
  699.       fprintf(stderr, "Invalid data following the -read-config flag\n");
  700.       show_usage();
  701.     }
  702.     count++;
  703.       }
  704.  
  705. #ifndef VMS
  706.     }
  707. #endif /* VMS */
  708.   }
  709.  
  710.   /* now do the configurations */
  711.   add_macro(2, CPP_GIVEN);
  712.   add_macro(1, CPP_GIVEN2);
  713.   do_config();
  714.  
  715.   /* go through the entire argument list */
  716.   while (count     < argc) {
  717.  
  718.     /* check for any switches */
  719.     if (is_switch(argv[count][0])) {
  720.  
  721.       /* call the routine for command line parsing */
  722.       if (argv[count][0] == '-') {
  723.     j = FALSE;
  724.       } else {
  725.     j = TRUE;
  726.       }
  727.       strcpy(cur_argstr, &(argv[count][1]));
  728.       parse_cmd(cur_argstr, j, TRUE);
  729.  
  730.     } else {
  731.  
  732.       /* treat it as a file name */
  733.       if (out_filenum == 1) {
  734.  
  735.     /* store the output file name */
  736.     strcpy(output_file[0], argv[count]);
  737.     strcpy(output_file[1], argv[count]);
  738.     out_filenum = 2;
  739.  
  740.       } else {
  741.  
  742.     /* check if both C and A are on */
  743.     if (get_option(OPT_FIRSTCOMMENT) &&
  744.         (get_option(OPT_SORTMODE) == SORT_ALL)) {
  745.       fprintf(stderr, "Sorting all functions and displaying file names does not mix\n");
  746.       show_usage();
  747.     }
  748.  
  749.     /* yes, a file has been parsed */
  750.     out_filenum = 3;
  751.  
  752.     /* process the file for input */
  753.     strcpy(file_name, argv[count]);
  754.     strcpy(errout_filename, file_name);
  755.     line_count = 1;
  756.  
  757.     /* check if the file exits */
  758.     if (stat( file_name, &fst ) != 0) {
  759.       sprintf(cur_argstr, "error: could not access file <%s>",
  760.           file_name);
  761.       err_msg(cur_argstr);
  762.       exit(1);
  763.     } else if ((fpin = fopen(file_name, "r")) == NULL) {
  764.       sprintf(cur_argstr, "error: could not read file <%s>",
  765.           file_name);
  766.       err_msg(cur_argstr);
  767.       exit(1);
  768.     }
  769.     fclose(fpin);
  770.  
  771.     /* build /lib/cpp line */
  772.     sprintf(sysl_out, "%s ", cpp_prog);
  773.     append_macros(sysl_out);
  774.     strcat(sysl_out, file_name);
  775.  
  776.     /* open it */
  777.     if ((fpin = open_input(sysl_out, "r")) == NULL) {
  778.       sprintf(tmp_str, "unable to open CPP \"pipe\" to file <%s>",
  779.           file_name);
  780.       err_msg(tmp_str);
  781.       exit(1);
  782.     }
  783.  
  784. #ifdef SETBUFFER
  785.     /* now set the buffer */
  786.     setbuffer(fpin, inbuffer, BUFFER_SIZE);
  787. #endif /* SETBUFFER */
  788.  
  789.     /* now do it */
  790.     parse_file();
  791.     files_parsed++;
  792.     if (get_option(OPT_SORTMODE) != SORT_ALL) {
  793.       send_file();
  794.     }
  795.     close_input(fpin);
  796.  
  797.       }
  798.  
  799.     }
  800.  
  801.     /* now go to the next argument */
  802.     count++;
  803.   }
  804.  
  805.   /* check if -o flag is unprocessed */
  806.   if (out_filenum == 1) {
  807.     fprintf(stderr, "No output file specified\n");
  808.     show_usage();
  809.   }
  810.   if ((out_filenum == 2) || (out_filenum == 0)) {
  811.     fprintf(stderr, "No files for parsing specified\n");
  812.     show_usage();
  813.   }
  814.  
  815.   /* send out everything, if it hasn't been done */
  816.   if (get_option(OPT_SORTMODE) == SORT_ALL) {
  817.     send_file();
  818.   }
  819.  
  820.   /* now close things up and combine if needed */
  821.   cxt_close();
  822.   exit(0);
  823.   return(0);
  824. }
  825.  
  826. /* transmit the currently stored comment to the output file */
  827. void
  828. send_first_comment(begin_str)
  829.   char *begin_str;
  830. {
  831.   int mode;
  832.  
  833.   /* check for last comment */
  834.   if (start_comment[0] != '\0') {
  835.  
  836.     /* check the mode */
  837.     if (doc_extract == DOC_NONE) {
  838.       mode = 0;
  839.     } else {
  840.       mode = 1;
  841.     }
  842.  
  843.     /* give the header for troff output */
  844.     dont_space = FALSE;
  845.     if (doc_extract == DOC_ROFF) {
  846.       start_block = TRUE;
  847.       init_roff(1);
  848.       if (total_out > 0) {
  849.     out_str(1, ".bp\n");
  850.     out_str(1, ".sp 0.5i\n");
  851.       }
  852.       out_str(1, ".KS\n");
  853.       out_str(1, ".nf\n");
  854.       out_str(1, ".ft 2\n");
  855.     } else {
  856.       out_char(mode, '\n');
  857.     }
  858.  
  859.     /* duplicate if needed */
  860.     if (!get_option(OPT_COMPACT) &&
  861.     (doc_extract == DOC_NONE) &&
  862.     (get_option(OPT_SHOWANYWAY) &&
  863.      get_option(OPT_BOTHUSE))) {
  864.       out_char(1, '\n');
  865.       out_str(0, begin_str);
  866.       out_str(1, begin_str);
  867.       out_str(0, start_comment);
  868.       out_str(1, start_comment);
  869.       out_char(0, '\n');
  870.       out_char(1, '\n');
  871.     } else {
  872.       out_str(mode, begin_str);
  873.       out_str(mode, start_comment);
  874.       out_char(mode, '\n');
  875.     }
  876.  
  877.   }
  878. }
  879.  
  880. #ifdef TEST
  881. /* dummy function that will never be used but makes a nice test */
  882. char (*(*test_it())[])(same_foo, x, y, fpfoo, z, fpp2)
  883.   int *x, z;
  884.   FILE* fpfoo, fpp2;
  885.   char (*(*same_foo())[])();
  886.   int *y;
  887. {
  888.   /* As scary a bit of code as I could think of.  Is it valid? */
  889. }
  890. #endif /*TEST*/
  891.